package com.atguigu.gulimall.product;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
//import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

/**
* 1、整合mybatis-plus
*       1)导入依赖
*       <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>
        2）配置
           1、配置数据源
             1）、导入数据库驱动
             2)、在application.yml中配置数据源相关信息
           2、配置mybatis-plus：
               1）、使用@MapperScan
                2）、告诉mybatis-plus，sql映射文件位置
* 2、逻辑删除
* 1)配置全局邏輯刪除規則 (高版本中可以忽略)
* 2）給Bean加上邏輯刪除註解@TableLogic
*
* 3、JSR303 : 规定了数据校验相关的标准
* 1）給Bean添加校驗註解:javax.validation.constraints
* 2)开启校验功能@Valid
*   效果：校验错误以后会有默认的响应
* 3）给校验的bean后紧跟一个BindingResult，用以获取校验结果
* 4）封装到R中统一返回
*
* 5）分组校验(多场景复杂校验。场景需求：比如对于品牌，新增时不能携带品牌id，因为它是自增的。而修改时就必须携带品牌id，否则我们不知道修改哪个品牌)
*       1）@Null(message = "新增不能指定id", groups = {AddGroup.class})
*           给校验注解标注什么情况需要进行校验
*       2）在对应的场景加上@Validated(value = {AddGroup.class})   如save
*       3）默认没有指定分组的校验注解，在分组校验情况下不生效，只会在@Validated情况下生效。
* 6）、自定义校验
*       1）、编写一个自定义的校验注解com.atguigu.common.valid.ListValue
*       2）、编写一个自定义的校验器 com.atguigu.common.valid.ListValueConstraintValidator
*       3）、关联自定义校验器和自定义的校验注解@Constraint(validatedBy = { ListValueConstraintValidator.class})
*
*
* 4、统一的异常处理
* @ControllerAdvice
* 1）编写异常处理类，使用@ControllerAdvice
* 2）使用@ExceptionHandler标注方法可以处理的异常
*
* 5、模板引擎
* 1)、thymeleaf-starter: 关闭缓存
* 2）、静态资源都放在static文件夹下可以按照路径直接访问
* 3）、页面在templates下，直接访问
*       springBoot，访问项目时，默认找index
* 4)不重启服务器的情况下实时更新
* 1)引入dev-tools
* 2）修改完页面ctrl+shift+f9重新自动编译当前页面  若修改了类或者配置，还是建议重新启动Application
*
*
* 6、整合redis
* 1）引入data-redis-starter
* 2）简单配置redis的host等信息
* 3）使用SpringBoot自动配置好的stringRedisTemplate来操作redis。
*
* 7、整合redisson作为分布式锁等功能框架
* 1）引入依赖
* <!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.12.0</version>
</dependency>
2）配置redisson
*
* 3)使用
* 参照文档做
*
* 8、整合SpringCache简化缓存开发
* 1）引入依赖
*spring-boot-starter-cache、 spring-boot-starter-data-redis
* 2）写配置
* （1）自动配置了哪一些
* CacheAutoConfiguration会导入RedisCacheConfiguration
* 自动配好了缓存管理器
* （2）配置使用redis作为缓存
* spring.cache.type=redis
* 3）测试使用缓存
* @Cacheable: Triggers cache population.  触发将数据保存到缓存的操作。  读数据，缓存中有则直接返回，缓存中没有读数据库并在缓存中保存
@CacheEvict: Triggers cache eviction.   触发将数据从缓存中删除的操作
@CachePut: Updates the cache without interfering with the method execution.  不影响方法执行更新缓存
@Caching: Regroups multiple cache operations to be applied on a method.  组合以上多个操作
@CacheConfig: Shares some common cache-related settings at class-level. ；在类级别共享缓存的相同配置
* 1）开启缓存功能
* 2）只需要使用注解就能完成缓存操作
*
* 4）原理
* CacheAutoConfiguration -> RedisCacheConfiguration -> 自动配置了RedisCacheManager -> 初始化所有的缓存
* -> 每个缓存决定使用什么配置 -> 如果redisCacheConfiguration有就用已有的，没有就用默认配置。
* -> 想改缓存的配置，只需要给容器中放一个RedisCacheConfiguration即可->就会运用到当前
* RedisCacheManager管理的所有缓存分区中
*
 * 5、模板引擎
 * 1)、thymeleaf-starter: 关闭缓存
 * 2）、静态资源都放在static文件夹下可以按照路径直接访问
 * 3）、页面在templates下，直接访问
 *       springBoot，访问项目时，默认找index
** 4)不重启服务器的情况下实时更新
 *  1)引入dev-tools
 *  2）修改完页面ctrl+shift+f9重新自动编译当前页面  若修改了类或者配置，还是建议重新启动Application
 *
** 6、整合redis
 *  1）引入data-redis-starter
 *  2）简单配置redis的host等信息
 *  3）使用SpringBoot自动配置好的stringRedisTemplate来操作redis。
 *
 *  7、整合redisson作为分布式锁等功能框架
 *         1. 锁的自动续期
 *         2. 不会有死锁问题, 加了过期时间
 *
 *     注意: 1. 如果指定了锁的超市时间,就使用脚本发送给redis数据库,执行脚本字符串, 不会自动续期
 *          2. 如果未指定, 则使用看门狗默认时间:
 *              启动一个定时任务,重新设置过期时间, 设置的新的时间就是看门狗的默认时间.
 *              internalLockLeaseTime[看门狗时间]
 *
 *
 * * 1）引入依赖
 * <!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
 * <dependency>
 *     <groupId>org.redisson</groupId>
 *     <artifactId>redisson</artifactId>
 *     <version>3.12.0</version>
 * </dependency>
 * 2）配置redisson
 *
 *  3)使用
 *  参照文档做
 *
 *  8、整合SpringCache简化缓存开发
 *  1）引入依赖
 * spring-boot-starter-cache、 spring-boot-starter-data-redis
 *  2）写配置
 *  （1）自动配置了哪一些
 *  CacheAutoConfiguration会导入RedisCacheConfiguration
 *  自动配好了缓存管理器
 *  （2）配置使用redis作为缓存
 *         spring.cache.type=redis
 *  3）测试使用缓存
 *     @Cacheable: Triggers cache population.  触发将数据保存到缓存的操作。  读数据，缓存中有则直接返回，缓存中没有读数据库并在缓存中保存
 *      @CacheEvict: Triggers cache eviction.   触发将数据从缓存中删除的操作
 *      @CachePut: Updates the cache without interfering with the method execution.  不影响方法执行更新缓存
 *      @Caching: Regroups multiple cache operations to be applied on a method.  组合以上多个操作
 *      @CacheConfig: Shares some common cache-related settings at class-level. ；在类级别共享缓存的相同配置
 *     1）开启缓存功能   @EnableCaching
 *     2）只需要使用注解就能完成缓存操作
 *
 *  4）原理
 *     CacheAutoConfiguration -> RedisCacheConfiguration -> 自动配置了RedisCacheManager -> 初始化所有的缓存
 *         -> 每个缓存决定使用什么配置 -> 如果redisCacheConfiguration有就用已有的，没有就用默认配置。
 *         -> 想改缓存的配置，只需要给容器中放一个RedisCacheConfiguration即可->就会运用到当前
 *  RedisCacheManager管理的所有缓存分区中
 *
 * 4、Spring-Cache的不足
 *           1）、读模式：
 *           缓存穿透：  查询一个null数据，spring.cache.redis.cache-null-values=true  不用担心
 *           缓存击穿：  大量并发进来同时查询一个正好过期的数据。解决： 加锁  默认是不加锁的。@Cacheable(sync = true)可加本地锁,但性能降低，
 *                                                                   其在判断缓存中有无数据时就加了锁，而不是说判断缓存中没有数据查询数据库时加锁。
 *           缓存雪崩：   大量的额key同时过期。解决：加随机时间。这里以加上过期时间作为解决方式，因为设置key的时间是随机的。
 *
 *           2）写模式（缓存与数据库的一致）  以下提供解决方案思路，spring-cache没有考虑到写模式以下解决方案。
 *             1）读写锁。
 *             2）引入canal，感知到mysql数据库去更新缓存
 *             3）读多写多，直接去数据库查询就行
 *
 *             总结：
 *             常规数据（读多写少，即时性，一致性要求不高的数据）：完全可以使用Spring-Cache。只要缓存的数据有过期时间就足够了。
 *             特殊数据特殊设计
 *     原理：
 *          CacheManager(RedisCacheManager)->Cache(RedisCache)->负责缓存的读写
 *
* //
* */


@EnableRedisHttpSession
@EnableCaching
@EnableFeignClients(basePackages = "com.atguigu.gulimall.product.feign")
@EnableDiscoveryClient
@MapperScan("com.atguigu.gulimall.product.dao")
@SpringBootApplication
public class GulimallProductApplication {

    public static void main(String[] args) {
        SpringApplication.run(GulimallProductApplication.class, args);
    }

}